package com.zh.mycommon.util;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 自定义的ID生成工具
 *
 * @author ZH
 * @date 15:11 2023/7/10
 */
public final class MyIdGenerator {
    private static final long DAY_MILLIS = 24 * 60 * 60 * 1000; // 一天的毫秒数
    private static final int NUMBER_DIGIT = 7;
    private static final NumberFormat number_format = new DecimalFormat("#" + "0".repeat(NUMBER_DIGIT));
    private static final Sequence generator_sequence;
    private static final DateTimeFormatter millisecond_format = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");

    static {
        try {
            generator_sequence = new Sequence(InetAddress.getLocalHost(), 1753342852746L);
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 生成随机的完整UUID
     * 长度32位
     *
     * @return uuid
     */
    public static String uuid() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 生成随机的简单UUID
     * 长度12位
     *
     * @return 随机字符串
     */
    public static String simpleUuid() {
        return UUID.randomUUID().toString().substring(24);
    }

    /**
     * 生成ID，根据时间毫秒值生成的递增ID
     * <br/>
     * 长度17位
     * 示例：20240330102206835
     * <br/>
     * 注意：不建议高并发使用，会生成重复的ID
     *
     * @return 数字字符串
     */
    public static String idByTimestamp() {
        return LocalDateTime.now().format(millisecond_format);
    }

    /**
     * 生成ID，根据日期+随机数的近似递增ID
     * <br/>
     * 长度16位：日期8+随机数8
     * <br/>
     * 注意：不建议高并发使用，会生成重复的ID
     *
     * @return 数字字符串
     */
    public static String idByDateRandom() {
        String dateStr = MyDateUtil.formatDate("yyyyMMdd", LocalDate.now());
        var randomNumber = number_format.format(Math.random() * Math.pow(10, NUMBER_DIGIT));
        return dateStr + randomNumber;
    }

    /**
     * 生成ID，根据分布式算法生成的递增ID
     * <br/>
     * 长度15~19位，
     * 例子：
     * 1773898467876737025
     * 1773898467876737026
     * 1773898467876737027
     *
     * @return 整数
     */
    public static synchronized long autoId() {
        return generator_sequence.nextId();
    }

    /*
     参考的com.baomidou.mybatisplus.core.toolkit.Sequence 的源码
     */
    static class Sequence {
        public static final String AT = "@";

        /**
         * 机器标识位数
         */
        private static final long workerIdBits = 5L;
        private static final long datacenterIdBits = 5L;
        private static final long maxWorkerId = ~(-1L << workerIdBits);
        private static final long maxDatacenterId = ~(-1L << datacenterIdBits);
        /**
         * 毫秒内自增位
         */
        private static final long sequenceBits = 12L;
        private static final long workerIdShift = sequenceBits;
        private static final long datacenterIdShift = sequenceBits + workerIdBits;
        /**
         * 时间戳左移动位
         */
        private static final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
        private static final long sequenceMask = ~(-1L << sequenceBits);

        private final long workerId;

        /**
         * 数据标识 ID 部分
         */
        private final long datacenterId;
        /**
         * 并发控制
         */
        private long seq = 0L;
        /**
         * 上次生产 ID 时间戳
         */
        private long lastTimestamp = -1L;

        private InetAddress inetAddress;

        /**
         * 时间起始标记点，作为基准，一般取系统的最近时间（一旦确定不能变动）
         */
        private final long twepoch;

        public Sequence(InetAddress inetAddress, long twepoch) {
            this.inetAddress = inetAddress;
            this.datacenterId = getDatacenterId();
            this.workerId = getMaxWorkerId(datacenterId);
            this.twepoch = twepoch;
        }

        /**
         * 有参构造器
         *
         * @param workerId     工作机器 ID
         * @param datacenterId 序列号
         */
        public Sequence(long workerId, long datacenterId, long twepoch) {
            this.workerId = workerId;
            this.datacenterId = datacenterId;
            this.twepoch = twepoch;
        }

        /**
         * 获取 maxWorkerId
         */
        protected long getMaxWorkerId(long datacenterId) {
            StringBuilder mpid = new StringBuilder();
            mpid.append(datacenterId);
            String name = ManagementFactory.getRuntimeMXBean().getName();
            if (!name.isBlank()) {
                /*
                 * GET jvmPid
                 */
                mpid.append(name.split(AT)[0]);
            }
            /*
             * MAC + PID 的 hashcode 获取16个低位
             */
            return (mpid.toString().hashCode() & 0xffff) % (Sequence.maxWorkerId + 1);
        }

        /**
         * 数据标识id部分
         */
        protected long getDatacenterId() {
            long id = 0L;
            try {
                NetworkInterface network = NetworkInterface.getByInetAddress(this.inetAddress);
                if (network == null) {
                    id = 1L;
                } else {
                    byte[] mac = network.getHardwareAddress();
                    if (null != mac) {
                        id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
                        id = id % (Sequence.maxDatacenterId + 1);
                    }
                }
            } catch (Exception ignored) {
            }
            return id;
        }

        /**
         * 获取下一个 ID
         *
         * @return 下一个 ID
         */
        public synchronized long nextId() {
            long timestamp = System.currentTimeMillis();
            //闰秒
            if (timestamp < lastTimestamp) {
                long offset = lastTimestamp - timestamp;
                if (offset <= 5) {
                    try {
                        this.wait(offset << 1);
                        timestamp = System.currentTimeMillis();
                        if (timestamp < lastTimestamp) {
                            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
                        }
                    } catch (InterruptedException ignored) {
                    }
                } else {
                    throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
                }
            }

            if (lastTimestamp == timestamp) {
                // 相同毫秒内，序列号自增
                seq = (seq + 1) & sequenceMask;
                if (seq == 0) {
                    // 同一毫秒的序列数已经达到最大
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                // 不同毫秒内，序列号置为 1 - 3 随机数
                seq = ThreadLocalRandom.current().nextLong(1, 3);
            }

            lastTimestamp = timestamp;

            // 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分
            return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | seq;
        }

        protected long tilNextMillis(long lastTimestamp) {
            long timestamp = System.currentTimeMillis();
            while (timestamp <= lastTimestamp) {
                timestamp = System.currentTimeMillis();
            }
            return timestamp;
        }
    }

}
